import mx.utils.Delegate;

import gugga.sequence.ExecuteAsyncMethodTask;
import gugga.sequence.FictiveTask;
import gugga.common.EventDescriptor;
import gugga.common.ITask;

import guggaLibTests.support.TaskExecutionEvaluator;

import test_framework.test.unit.TestCase;

/**
 * @author Krasimir
 */
class guggaLibTests.ExecuteAsyncMethodTaskTest 
		extends TestCase 
{
	private var mTestParameter1:String;
	private var mTestParameter2:Number;
	
	private var delayInterval:Number;
	
	public function ExecuteAsyncMethodTaskTest()
	{
		super();
		this.delayInterval = 1000;
	}
	
	public function testCreate()
	{
		// initialize TaskExecutionEvaluator:
		var evaluator:TaskExecutionEvaluator = new TaskExecutionEvaluator(this);
		evaluator.setDelayedEvaluation(this.delayInterval);
		
		var eventSource:FictiveTask = new FictiveTask();
		var eventDescriptor:EventDescriptor = new EventDescriptor(
				eventSource, 
				"start");
		var task:ITask = ExecuteAsyncMethodTask.create(
				eventDescriptor,
				this,
				this.triggerCreateTask,
				[eventSource]);
		task.addEventListener(
				"start", 
				Delegate.create(evaluator, evaluator.onTaskStarted));
		task.addEventListener(
				"completed", 
				Delegate.create(evaluator, evaluator.onTaskCompleted));
		
		assertNotNull("task must be not null after creation", task);
		assertNotUndefined("task must be defined after creation", task);
		
		task.start();
		
		evaluator.registerRequiredTaskStatus({task:task, event:"start"});
		evaluator.registerRequiredTaskStatus({task:task, event:"completed"});
		
		evaluator.pauseToEvaluateTaskExecution();
	}
	
	public function triggerCreateTask(aTriggerCondition:ITask)
	{
		assertNotNull("argumets must be valid", aTriggerCondition);
		assertNotUndefined("argumets must be valid", aTriggerCondition);
		assertInstanceOf(aTriggerCondition, ITask);
		
		aTriggerCondition.start();
	}
	
	public function testCreateBasic()
	{
		// initialize TaskExecutionEvaluator:
		var evaluator:TaskExecutionEvaluator = new TaskExecutionEvaluator(this);
		evaluator.setDelayedEvaluation(this.delayInterval);
		
		var completionEventName:String = "start";
		var eventSource:FictiveTask = new FictiveTask();
		var task:ITask = ExecuteAsyncMethodTask.createBasic(
				completionEventName, 
				eventSource, 
				this.triggerCreateBasicTask, 
				[eventSource]);
		task.addEventListener(
				"start", 
				Delegate.create(evaluator, evaluator.onTaskStarted));
		task.addEventListener(
				"completed", 
				Delegate.create(evaluator, evaluator.onTaskCompleted));
		
		assertNotNull("task must be not null after creation", task);
		assertNotUndefined("task must be defined after creation", task);
		
		task.start();
		
		evaluator.registerRequiredTaskStatus({task:task, event:"start"});
		evaluator.registerRequiredTaskStatus({task:task, event:"completed"});
		
		evaluator.pauseToEvaluateTaskExecution();
	}
	
	public function triggerCreateBasicTask(aTriggerCondition:ITask)
	{
		assertNotNull("argumets must be valid", aTriggerCondition);
		assertNotUndefined("argumets must be valid", aTriggerCondition);
		assertInstanceOf(aTriggerCondition, ITask);
		
		aTriggerCondition.start();
	}
	
	// This test will assure that the forwarding sequence of the specified 
	// parameters is the same as the parameter sequence in the specified array
	public function testMultipleParametersForwarding()
	{
		var eventSource:FictiveTask = new FictiveTask();
		var eventDescriptor:EventDescriptor = new EventDescriptor(
				eventSource, 
				"start");
		
		mTestParameter1 = new String("value1");
		mTestParameter2 = new Number(13);
		
		var task:ITask = ExecuteAsyncMethodTask.create(
				eventDescriptor,
				this,
				this.triggerMultipleParametersTaskCondition,
				[eventSource, mTestParameter1, mTestParameter2]);
				
		task.start();
	}
	
	public function triggerMultipleParametersTaskCondition(
			aTriggerCondition:ITask,
			aTestParameter1:String,
			aTestParameter2:Number)
	{
		assertNotNull("argument must not be NULL", aTriggerCondition);
		assertNotUndefined("argument must be defined", aTriggerCondition);
		assertInstanceOfWithMessage(
				"argument must be of a valid type", 
				aTriggerCondition, 
				ITask);
		
		assertNotNull("argument must not be NULL", aTestParameter1);
		assertNotUndefined("argument must be defined", aTestParameter1);
		assertInstanceOf("argument must be of a valid type", aTestParameter1, String);
		assertSame("arguments should match", aTestParameter1, mTestParameter1);
		
		assertNotNull("argument must not be NULL", aTestParameter2);
		assertNotUndefined("argument must be defined", aTestParameter2);
		assertInstanceOf("argument must be of a valid type", aTestParameter2, Number);
		assertSame("arguments should match", aTestParameter2, mTestParameter2);
		
		aTriggerCondition.start();
	}
	
	public function testIsImmediatelyInterruptable()
	{
		var eventSource:FictiveTask = new FictiveTask();
		var eventDescriptor:EventDescriptor = new EventDescriptor(
				eventSource, 
				"start");
		var task:ITask = ExecuteAsyncMethodTask.create(
				eventDescriptor,
				this,
				this.triggerEmptyFunction,
				[]);
				
		assertTrue(
				"ExecuteAsyncMethodTask.isImmediatelyInterruptable() should be always interrupt immediately", 
				task.isImmediatelyInterruptable());
	}
	
	private function triggerEmptyFunction()
	{
		// THIS FUNCTION SHOULD BE EMPTY!!!
	}
	
	/**
	 * OLD: Assure <code>interrupt()</code> will trigger <b><i>interrupted</i></b> 
	 * event instead of <b><i>completed</i></b> event. In other circumstances 
	 * <b><i>interrupted</i></b> event should not be raised.
	 * CURRENT: Assure <code>interrupt()</code> will immediately trigger 
	 * <b><i>interrupted</i></b> event instead of <b><i>completed</i></b> event. 
	 * Event <b><i>completed</i></b> should not be raised at all.
	 */
	public function testInterrupt()
	{
		// initialize TaskExecutionEvaluators:
		var evaluatorBeforeInterrupt:TaskExecutionEvaluator 
			= new TaskExecutionEvaluator(this);
		var evaluatorAfterInterrupt:TaskExecutionEvaluator 
			= new TaskExecutionEvaluator(this);
		
		// test portion before interrupt():
		evaluatorBeforeInterrupt.setDelayedEvaluation(this.delayInterval);
		
		var eventSource:FictiveTask = new FictiveTask();
		var eventDescriptor:EventDescriptor = new EventDescriptor(
				eventSource, 
				"start");
		var task:ITask = ExecuteAsyncMethodTask.create(
				eventDescriptor,
				this,
				this.triggerEmptyFunction,
				[]);
		task.addEventListener(
				"start", 
				Delegate.create(
						evaluatorBeforeInterrupt, 
						evaluatorBeforeInterrupt.onTaskStarted));
		task.addEventListener(
				"interrupted", 
				Delegate.create(
						evaluatorBeforeInterrupt, 
						evaluatorBeforeInterrupt.onTaskInterrupted));
		task.addEventListener(
				"completed", 
				Delegate.create(
						evaluatorAfterInterrupt, 
						evaluatorAfterInterrupt.onTaskCompleted));
		
		task.start();
		evaluatorBeforeInterrupt.registerRequiredTaskStatus(
				{task:task, event:"start"});
		
		task.interrupt();
		evaluatorBeforeInterrupt.registerRequiredTaskStatus(
				{task:task, event:"interrupted"});
		
		evaluatorBeforeInterrupt.pauseToEvaluateTaskExecution();
		// assure interrupted event has not been raised:
		evaluatorBeforeInterrupt.assertTaskStatusRegisterEqualsRequiredTaskStatusRegister(
				"interrupted event should be raised immediately");
		
		// test portion after interrupt():
		evaluatorAfterInterrupt.setDelayedEvaluation(this.delayInterval);
		
		// complete the ExecuteAsyncMethodTask:
		eventSource.start();
		
		evaluatorAfterInterrupt.pauseToEvaluateTaskExecution();
		// assure completed event has not been raised:
		evaluatorAfterInterrupt.assertTaskStatusRegisterIsEmpty(
				"completed event should not be raised at all - the task is already interrupted");
	}
}